#include "stdafx.h"
#include "model\Division.h"
#include "lib\TCollection.h"
#include "model\Match.h"
#include "model\Team.h"


///
/// Constructor, default
///
Division::Division() {};

///
/// Constructor: std::string name
///
Division::Division(std::string name) {
  name_ = name;
}


///
/// Accessor for Division::name_
///
std::string Division::name() const {
  return name_;

}

///
/// Add team to Team set
///
Division::IterBoolPair Division::AddTeam(Team& team) {
  return teams_.Add(team);
}

///
/// Find team in Team set
///
Division::IterBoolPair Division::FindTeam(Team& team) const {
  return teams_.Find(team);
}

///
/// Get collection (elements) of Teams
///
TCollection<Team>::CollectionPair Division::GetTeams() const {
  return teams_.GetCollection();
}

///
/// Delete team in Team set
///
void Division::DeleteTeam(std::set<Team>::iterator iter) {
  teams_.Delete(iter);
}

///
/// Get number of teams in Team set
///
int Division::NumTeams() const {
  return teams_.Count();
}

///
/// Find a match in Match map
///

Division::MatchBoolPair Division::FindMatch(Match& match) const {
  OuterMatchMap::const_iterator outerit = matches_.find(match.home_team());
  InnerMatchMap::const_iterator innerit;
  bool found = false;
  if (outerit != matches_.end()) {
    innerit = outerit->second.find(match.guest_team());
    if (innerit != outerit->second.end()) {
      found = true;
    }
  }
  return make_pair(innerit, found);
}

///
/// Get iterator pair from the outer level of Match map
///
Division::OuterMatchPair Division::GetMatchIters() const {
  return make_pair(matches_.begin(), matches_.end());
}

///
/// Get iterator pair from the inner level of Match map
///
Division::InnerMatchPair Division::GetMapMatches(const OuterMatchMap::const_iterator iter) const {
  return make_pair(iter->second.begin(), iter->second.end());
}

///
/// Get number of matches in Match map
///
int Division::NumMatches() const {
  int size = 0;
  OuterMatchPair iters = GetMatchIters();
  for (; iters.first != iters.second; ++iters.first) {
    size += iters.first->second.size();
  }
  return size;
}

///
/// Add match to Match map
///
void Division::AddMatch(const Match& match) {
  matches_[match.home_team()][match.guest_team()] = match;
}


///
/// Add result to Match map
///
void Division::AddResult(const Match& result) {
  matches_[result.home_team()][result.guest_team()] = result;

}



// TODO implement ValidateResult()
bool Division::ValidateResult(Match& res) const {
  if (!FindTeam(Team(res.home_team())).second)
    res.AddError("Home team does not exist: " + res.home_team());
  if (!FindTeam(Team(res.guest_team())).second)
    res.AddError("Guest team does not exist: " + res.guest_team());
  Division::MatchBoolPair matchiter = FindMatch(res);
  if (matchiter.second) {
    Match match = matchiter.first->second;
    if (res.date() != match.date()) {
      res.AddError("Invalid match date: " + res.date());
    } else if (match.HasResult()) {
      res.AddError("Result is already registered.");
    } else if (res.home_score() < 0 || res.guest_score() < 0) {
      res.AddError("Score cannot be less than 0.");
    }
  }
  return !res.HasErrors();
}


///
/// Overloaded lessthen used by std::set
///
bool Division::operator<(const Division &division) const {
  return name_ < division.name();
}

///
/// Overloded equals operator 
///
int Division::operator==(const Division &division) const {
  return name_.compare(division.name());
}


///
/// Attach calculator to Division object
///
void Division::InjectCalculator(PointCalculator *calc) {
  calc_ = calc;
}



///
/// Performs validation on the models data
///
bool Division::Validate() {
  Validator v(this);
  v.Inject(name_, "Name").Length(3, 50).Chars(NameChars()).Required();
  return errors().empty();
}


///
/// Returns a string conntaining valid characters for the name_ attribute
///
std::string Division::NameChars() {
  return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-\x91\x92\x9B\x9D\x86\x8F ";
}

///
/// Friendly insertion operator for Division
///
std::istream& operator>>(std::istream& is,  Division& division) {
  std::getline(is, division.name_);
  int n;
  is >> n;
  is.ignore();
  for (int i = 0; i < n; ++i) {
    Team team;
    is >> team;
    if (team.Validate())
      division.AddTeam(team);
  }
  is >> n;
  is.ignore();
  for (int i = 0; i < n; ++i) {
    Match match;
    is >> match;
    if (match.Validate())
      division.AddMatch(match);
  }
  return is;
}

///
/// Friendly extraction operator for Division
///
std::ostream& operator<<(std::ostream& os, const Division& division) {
  os << division.name_ << '\n' << division.NumTeams() << '\n';
  TCollection<Team>::CollectionPair iters = division.GetTeams();
  for (; iters.first != iters.second; ++iters.first) {
    os << *iters.first << '\n';
  }
  os << division.NumMatches();
  Division::OuterMatchPair teams = division.GetMatchIters();
  for (; teams.first != teams.second; ++teams.first) {
    Division::InnerMatchPair matchiters = division.GetMapMatches(teams.first);
    for (; matchiters.first != matchiters.second; ++matchiters.first){
      os << '\n' << matchiters.first->second;
    }
  }
  return os;
}




